package main

import (
	"errors"
	"fmt"
	"reflect"
)

type Params struct {
	Name string
	Age  int
	User *User
}

type User struct {
	Username string
	//Password string
}

func main() {
	var params Params
	//params.Name = "123"
	params.User = &User{}

	fmt.Println("Explicit")
	if err := Explicit(params, "Name", "Age", "Username", "Password"); err != nil {
		panic(err)
	}

	fmt.Println("ExplicitForce")
	if err := ExplicitForce(params, "Name", "Age", "Username", "Password"); err != nil {
		panic(err)
	}
}

func Explicit(params interface{}, ignore ...string) error {
	v := reflect.ValueOf(params)
	exp := newExp(ignore, false)
	return exp.explicit(v)
}

// 强制验证ignore中是否有多余的成员
func ExplicitForce(params interface{}, ignore ...string) error {
	v := reflect.ValueOf(params)
	exp := newExp(ignore, true)
	return exp.explicit(v)
}

type exp struct {
	m         map[string]struct{}
	ignore    []string
	execForce bool
}

func newExp(ignore []string, force bool) exp {
	return exp{
		m:         map[string]struct{}{},
		ignore:    ignore,
		execForce: force,
	}
}

func (e *exp) explicit(v reflect.Value) error {
	if v.CanInterface() {
		t := v.Type()
		switch t.Kind() {
		case reflect.Ptr:
			if err := e.explicit(v.Elem()); err != nil {
				return err
			}
		case reflect.Struct:
			for i := 0; i < t.NumField(); i++ {
				f := v.Field(i)
				if f.Kind() == reflect.Struct || f.Kind() == reflect.Ptr {
					//fmt.Println(t.Field(i).Name, f.Type())
					if err := e.explicit(f); err != nil {
						return err
					}
				} else {
					if f.CanInterface() {
						if e.ignoreKeyExist(t.Field(i).Name) {
							continue
						} else {
							switch f.Type().Kind() {
							case reflect.String:
								if f.Interface() == "" {
									return errors.New(t.Field(i).Name + " is \"\"")
								}
							case reflect.Int8, reflect.Int16,
								reflect.Int32, reflect.Int64, reflect.Int,
								reflect.Float32, reflect.Float64:
								if f.Interface() == 0 {
									return errors.New(t.Field(i).Name + " is zero")
								}
							}
						}
						//fmt.Println("CanInterface\t", t.Field(i).Name, f.Type(), "Interface: ", f.Interface())
					} else {
						fmt.Println("CanInterface else", t.Field(i).Name, f.Type(), f)
					}
				}
			}
		}
	}
	if e.execForce {
		if err := e.force(); err != nil {
			return err
		}
	}
	return nil
}

func (e *exp) ignoreKeyExist(key string) bool {
	e.m[key] = struct{}{}
	for _, v := range e.ignore {
		if v == key {
			return true
		}
	}
	return false
}

func (e *exp) force() error {
	for _, v := range e.ignore {
		var exist bool
		for k, _ := range e.m {
			if k == v {
				exist = true
				break
			}
		}
		if !exist {
			return errors.New(v + " is not match")
		}
	}
	return nil
}
